#include <string>

#include <boost/python.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/system/error_code.hpp>
#include <boost/python/copy_non_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>

#include <common/logger.hpp>

#include <lib/scanner.hpp>
#include <lib/protocol.hpp>
#include <lib/scan_struct.hpp>
#include <lib/parallel_scanner.hpp>
#include <lib/client_environment.hpp>
#include <lib/meta_tablet_scanner.hpp>
#include <lib/io_service_pool_impl.hpp>

//#include "py_boost_function.hpp"

using namespace boost::python;
using namespace xbase;

typedef boost::system::error_code error_code;
typedef protocol::tablet_info tablet_info;
typedef protocol::server_address server_address;

void init_log_config(const char* log_config_path)
{
	LOG_CONFIG(log_config_path);
}

void log_output(const char* log_string)
{
	LOG(INFO, log_string);
}

client_options_ptr create_client_options_ptr()
{
	return client_options_ptr(new client_options);
}

io_service_pool_ptr create_io_service_pool_ptr()
{
	return io_service_pool_ptr(new impl::single_io_service);
}

row_result_ptr create_row_result_ptr()
{
	return row_result_ptr(new row_result);
}

BOOST_PYTHON_MODULE(pyxbase)
{
	register_ptr_to_python<scanner_ptr>();
	register_ptr_to_python<row_result_ptr>();
	register_ptr_to_python<client_options_ptr>();
	register_ptr_to_python<io_service_pool_ptr>();
	register_ptr_to_python<parallel_scanner_ptr>();
	//register_ptr_to_python<row_result_ptr>();

	// wrap client_options
	class_<client_options>("client_options")
		.def_readwrite("retry_times", &client_options::retry_times)
		.def_readwrite("retry_interval", &client_options::retry_interval)
		.def_readwrite("stat_dump_interval", &client_options::stat_dump_interval)
		.def_readwrite("stat_file_name", &client_options::stat_file_name)
		;

	// warp io_service_pool
	class_<io_service_pool, boost::noncopyable>("io_service_pool", no_init);

	// wrap scan_info
	class_<scan_info>("scan_info", init<std::string, std::string, std::string>())
		.def("add_column", &scan_info::add_column)
		.def_readwrite("m_table_name", &scan_info::m_table_name)
		.def_readwrite("m_start_row", &scan_info::m_start_row)
		.def_readwrite("m_end_row", &scan_info::m_end_row)
		//.def_readwrite("m_column_names", &scan_info::m_column_names)
		.def_readwrite("m_latest_time_stamp", &scan_info::m_latest_time_stamp)
		.def_readwrite("m_oldest_time_stamp", &scan_info::m_oldest_time_stamp)
		.def_readwrite("m_ttl", &scan_info::m_ttl)
		.def_readwrite("m_version_number", &scan_info::m_version_number)
		.def_readwrite("m_options", &scan_info::m_options)
		.def_readwrite("m_num_row", &scan_info::m_num_row)
		.def_readwrite("m_row_offset", &scan_info::m_row_offset)
		.def_readwrite("m_row_count", &scan_info::m_row_count)
		;

	// callback function for scan operation
	//def_function<void(row_result::ptr_type, error_code)>("scan_callback");
	//def_function<void()>("test_handler");

	// wrap error_code
	class_<error_code>("error_code")
		.def("value", &error_code::value)
		.def("message", &error_code::message)
		;

	// wrap cell
	class_<cell>("cell")
		.def("get_version", &cell::get_version)
		.def("get_version_count", &cell::get_version_count)
		;

	// wrap row_result
	class_<row_result>("row_result")
		.def("get_cell", &row_result::get_cell)
		.def_readwrite("m_row_name", &row_result::m_row_name)
		.def_readwrite("m_cell_vector", &row_result::m_cell_vector)
		;

	// wrap tablet_info
	class_<tablet_info>("tablet_info")	
		.def_readwrite("m_table_name", &tablet_info::m_table_name)
		.def_readwrite("m_tablet_id", &tablet_info::m_tablet_id)
		.def_readwrite("m_start_row", &tablet_info::m_start_row)
		.def_readwrite("m_end_row", &tablet_info::m_end_row)
		.def("is_online", &tablet_info::status_is_online)
		.def("is_deleted", &tablet_info::status_is_deleted)
		.def("is_split", &tablet_info::status_is_split)
		;

	// wrap server_address
	class_<server_address>("server_address")
		.def_readwrite("m_hostname", &server_address::m_hostname)
		.def_readwrite("m_port", &server_address::m_port)
		.def_readwrite("m_startcode", &server_address::m_startcode)
		;

	// wrap scanner
	class_<scanner, boost::noncopyable>("scanner",
		init<io_service_pool&, session_manager&, location_cache&, scan_info&>())
		.def("async_get_next_row", &scanner::async_get_next_row)
		.def("get_next_row", &scanner::get_next_row)
		;

	// wrap parallel_scanner
	class_<parallel_scanner, boost::noncopyable>("parallel_scanner",
		init<io_service_pool&, session_manager&, location_cache&, scan_info&, parallel_scanner::handler_type>())
		.def("start", &parallel_scanner::start)
		;

	// wrap client_environment
	class_<client_environment, boost::noncopyable>("client_environment",
		init<client_options_ptr, io_service_pool_ptr, const char*, unsigned short>())
		.def("new_scanner", &client_environment::new_scanner)
		.def("new_parallel_scanner", &client_environment::new_parallel_scanner)
		.def("new_meta_tablet_scanner", &client_environment::new_meta_tablet_scanner)
		.def("get_root_meta_cache", &client_environment::get_root_meta_cache,
			return_value_policy<copy_non_const_reference>())
		;

	// wrap root_meta_cache
	class_<root_meta_cache, boost::noncopyable>("root_meta_cache", no_init)
		.def("get_root_meta", &root_meta_cache::get_root_meta)
		;

	// wrap meta_tablet_scanner
	class_<meta_tablet_scanner, boost::noncopyable>("meta_tablet_scanner", no_init)
		.def("get_next_row", &meta_tablet_scanner::get_next_row)
		;

	def("log_output", &log_output);
	def("init_log_config", &init_log_config);
	def("create_row_result_ptr", &create_row_result_ptr);
	def("create_client_options_ptr", &create_client_options_ptr);
	def("create_io_service_pool_ptr", &create_io_service_pool_ptr);
}
